package algorithm;

import datastructure.Fraction;
import datastructure.Pair;
import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import utility.FileReader;
import utility.InstancesConverter;
import weka.classifiers.Classifier;
import weka.classifiers.Evaluation;
import weka.classifiers.lazy.IBk;
import weka.classifiers.trees.Id3;
import weka.core.Instance;
import weka.core.Instances;

public class KNearestNeighbourModel {

    private Instances data;
    private int k;
    private IBk model;

    public KNearestNeighbourModel(Instances data, int k) {
        this.data = data;
        this.k = k;
    }

    public KNearestNeighbourModel(IBk model) {
        this.model = model;
    }

    public void setK(int k) {
        this.k = k;
    }

    public String executeInstance(Instance instance) {
        int temp_distance = 0;
        ArrayList<Pair<Integer, Integer>> list_of_distance = new ArrayList<Pair<Integer, Integer>>();
        
        for (int row = 0; row < data.numInstances(); row++) {
            temp_distance = 0;
            for (int attribute = 1; attribute < instance.numValues(); attribute++) {
                if (!instance.stringValue(attribute).equals(data.instance(row).stringValue(attribute))) {
                    temp_distance++;
                }
            }
            Pair<Integer, Integer> distance = Pair.makePair(temp_distance, row);
            list_of_distance.add(distance);
        }

        Collections.sort(list_of_distance);

        int count1 = 0, count0 = 0;
        for (int row = 0; row < k; row++) {
            int index = list_of_distance.get(row).second;
            if ("1".equals(data.instance(index).stringValue(0))) {
                count1++;
            } else {
                count0++;
            }
            
        }

        return (count1 > count0) ? "1.0" : "0.0";
    }

    public String executeInstanceWEKA(Instance instance) {
        try {
            return Double.toString(model.classifyInstance(instance));
        } catch (Exception e) {
            System.out.println(e);
            return "";
        }
    }

    public static void prin(int[] a) {
        int i = 0;
        while (i < a.length) {
            System.out.print(a[i]);
            System.out.print(" ,");
            i++;
        }
        System.out.println("");
    }

    public String crossval(int fold, int knn, Instances dat) {
        int i = 0;
        int j = 0;
        int k = 0;
        int bagi = (dat.numInstances() / fold);
        int[] hasil = new int[fold];

        String asss = "kosong.arff";
        Instances qwe = FileReader.readFile(asss);
        while (i < fold) {
            Instances tests = new Instances(qwe);
            tests.setClassIndex(0);
            int z = i * (dat.numInstances() / fold);
            while (z < ((i + 1) * bagi)) {
                Instance piece = dat.instance(z);
                tests.add(piece);
                z++;
            }
            KNearestNeighbourModel model1 = new KNearestNeighbourModel(tests,knn);
            //mbuat model dati tests
            if (i == 0) {
                j = bagi;
                while (j < dat.numInstances()) {
                    Instance train = dat.instance(j);
                    String harus = train.toString();
//                    System.out.println(model1.executeInstance(train).equals(harus.substring(0, 1)+".0")+"HAHAHAHA");
                    if (!model1.executeInstance(train).equals(harus.substring(0, 1)+".0")) {
                        hasil[i] = hasil[i] + 1;
                    }
                    j++;
                }
            } else if (i == (fold - 1)) {
                j = 0;

                while (j < ((fold - 1) * bagi)) {
                    Instance train = dat.instance(j);
                    String harus = train.toString();
                    if (!model1.executeInstance(train).equals(harus.substring(0, 1)+".0")) {
                        hasil[i] = hasil[i] + 1;
                    }
                    j++;
                }

            } else {
                j = 0;
                while (j < (i * bagi)) {
                    Instance train = dat.instance(j);
                    String harus = train.toString();
                    if (!model1.executeInstance(train).equals(harus.substring(0, 1)+".0")) {
                        hasil[i] = hasil[i] + 1;
                    }
                    j++;
                }
                j = ((i + 1) * bagi);
                while (j < dat.numInstances()) {
                    Instance train = dat.instance(j);
                    String harus = train.toString();
                    if (!model1.executeInstance(train).equals(harus.substring(0, 1)+".0")) {
                        hasil[i] = hasil[i] + 1;
                    }
                    j++;
                }
            }

            j = 0;
            i++;
        }

//        System.out.println("Correctly classified instances   : " +rata(hasil)+ " instances    " +((double)rata(hasil)/(double)dat.numInstances()*100)+"%" );
//        System.out.println("Incorrectly classified instances : " +(dat.numInstances()-rata(hasil))+ " instances    " +((double)(dat.numInstances()-rata(hasil))/(double)dat.numInstances()*100)+"%" );
//        System.out.println("Root mean square error           : " +rms(accu(invers(hasil,dat.numInstances()),dat.numInstances())));
//        System.out.println("Number of Instances              : " +dat.numInstances() +" instances");
        String hasil1 = "Correctly classified instances   : " +rata(hasil)+ " instances    " +((double)rata(hasil)/(double)dat.numInstances()*100)+"%"+"\n";
        String hasil2 = "Incorrectly classified instances : " +(dat.numInstances()-rata(hasil))+ " instances    " +((double)(dat.numInstances()-rata(hasil))/(double)dat.numInstances()*100)+"%\n" ;
        String hasil3 = "Root mean square error           : " +rms(accu(invers(hasil,dat.numInstances()),dat.numInstances()))+"\n" ;
        String hasil4 = "Number of Instances              : " +dat.numInstances() +" instances\n";
        String shasil = hasil1+hasil2+hasil3+hasil4;
        return shasil;
    }

    public String crossv(int fold, Instances dat) {
        Classifier cls = new IBk();
        String hasil = "cross validation failed";
        try {
            cls.buildClassifier(dat);
            Evaluation eval = new Evaluation(dat);
            eval.crossValidateModel(cls, dat, fold, new Random(1));
            hasil = (eval.toSummaryString("\nResults\n======\n", false));
        } catch (Exception ex) {
            Logger.getLogger(NaiveBayesianModel.class.getName()).log(Level.SEVERE, null, ex);
        }
        return hasil;
    }
    public static int[] invers(int[] nums, int jum){
        for (int i = 0; i < nums.length; i++) {
            nums[i] = jum - nums[i];
        }
        return nums;
    }

    public static double rms(int[] nums) {
        double ms = 0;
        for (int i = 0; i < nums.length; i++) {
            ms += nums[i] * nums[i];
        }
        ms /= nums.length;
        return Math.sqrt(ms);
    }
    public static double rms(double[] nums){
        double ms = 0;
        for (int i = 0; i < nums.length; i++)
            ms += nums[i] * nums[i];
        ms /= nums.length;
        return Math.sqrt(ms);
    }
    public static int rata(int[] nums){
        int ms = 0;
        for (int i = 0; i < nums.length; i++) {
            ms = ms+ nums[i];
        }
        ms = ms / nums.length;
        return ms;
    }
    public static double[] accu(int[] nums , int jum){
        double[] acc = new double[nums.length];
        for (int i = 0; i < nums.length; i++) {
            acc[i] = ((double)nums[i]/(double)jum);
        }
        return acc;
    }

    public static void main(String[] asdf) {

        String a = "C:\\AI\\specthearttrain.arff";
        FileReader file = new FileReader();
        int k = 1;
        Instances qwe = file.readFile(a);
        qwe.setClassIndex(0);
        KNearestNeighbour asd = new KNearestNeighbour();
        KNearestNeighbourModel zxc = asd.KNearestNeighbourWEKA(qwe);
        System.out.println(qwe);
        String hasil1 = zxc.crossv(5,qwe);
        String hasil = zxc.crossval(5, k, qwe);
       System.out.println(hasil1);
       System.out.println(hasil);
        //System.out.println(rata(hasil));
 //       double[] accu = accu(hasil,qwe.numInstances());
 //       System.out.println(rata(hasil));
 //       System.out.println(rms(accu(hasil,qwe.numInstances())));

    }
  
    @Override
    public String toString() {
        return data.toString() + " " + k;
    }

    public void saveModel(String path) throws IOException {
        Object[] test = new Object[2];
        test[0] = data;
        test[1] = k;
        ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream(path));
        oos.writeObject(test);
        oos.flush();
        oos.close();
    }

    public void loadModel(String path) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream(path));
        Object[] test = (Object[]) ois.readObject();
        data = (Instances) test[0];
        k = (Integer) test[1];
        ois.close();
    }

    public void saveModelWEKA(String path) throws Exception {
        weka.core.SerializationHelper.write(path, model);
    }

    public void loadModelWEKA(String path) throws Exception {
        model = (IBk) weka.core.SerializationHelper.read(path);
    }
}
